"use strict";
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
const vue = require("vue");
const index = require("../loading/index.cjs");
const _hoisted_1 = { class: "me-virtual-scroll-wrap" };
const _hoisted_2 = ["id", "data-index"];
const _sfc_main = /* @__PURE__ */ vue.defineComponent({
  ...{
    name: "me-virtual-scroll"
  },
  __name: "virtual-scroll",
  props: {
    arrayList: { default() {
      return [];
    } },
    itemHeight: { default: 40 },
    bufferScale: { default: 1 },
    height: { default: "100%" },
    showLoading: { type: Boolean, default: false },
    itemGap: { default: 16 },
    scrollBottomLoading: { type: Boolean, default: false }
  },
  emits: ["scroll"],
  setup(__props, { expose: __expose, emit: __emit }) {
    const props = __props;
    const emits = __emit;
    const screenHeight = vue.ref(0);
    const start = vue.ref(0);
    const end = vue.ref(0);
    const containerRef = vue.ref();
    const listRef = vue.ref();
    const listParentRef = vue.ref();
    let positions = [];
    const overflow = vue.ref("auto");
    const offset = vue.ref(0);
    const isLoading = vue.ref(false);
    const isTopOrBottom = vue.ref("top");
    let isScrollBottom = true;
    let count = 0;
    const list = vue.computed(() => {
      return props.arrayList.map((item, index2) => {
        return {
          index: index2,
          item
        };
      });
    });
    const visibleCount = vue.computed(() => {
      return Math.ceil(screenHeight.value / props.itemHeight);
    });
    const aboveCount = vue.computed(() => {
      return Math.min(start.value, props.bufferScale * visibleCount.value);
    });
    const belowCount = vue.computed(() => {
      return Math.min(props.arrayList.length - end.value, props.bufferScale * visibleCount.value);
    });
    const visibleData = vue.computed(() => {
      const start0 = start.value - aboveCount.value;
      const end0 = end.value + belowCount.value;
      return list.value.slice(start0, end0);
    });
    let observer = new ResizeObserver((entries) => {
      for (let i = 0; i < entries.length; i++) {
        handleResize(entries[i].target);
      }
    });
    const handleResize = (el) => {
      const height = el.getBoundingClientRect().height;
      const index2 = Number(el.getAttribute("data-index"));
      if (positions[index2]) {
        positions[index2].height = height;
        positions[index2].bottom = positions[index2].top + height;
        for (let i = index2 + 1; i < positions.length; i++) {
          positions[i].index = i;
          positions[i].top = positions[i - 1].bottom;
          positions[i].bottom = positions[i].top + positions[i].height;
        }
        if (positions[count]) {
          containerRef.value.scrollTop = positions[count].top;
          start.value = getStartIndex(containerRef.value.scrollTop);
          end.value = start.value + visibleCount.value;
          updateHeight();
          setStartOffset();
        }
      }
    };
    const getStartIndex = (scrollTop = 0) => {
      return binarySearch(positions, scrollTop);
    };
    const binarySearch = (list2, value) => {
      let start2 = 0;
      let end2 = list2.length - 1;
      let tempIndex = null;
      while (start2 <= end2) {
        let midIndex = parseInt(String((start2 + end2) / 2));
        let midValue = list2[midIndex].bottom;
        if (midValue === value) {
          return midIndex + 1;
        } else if (midValue < value) {
          start2 = midIndex + 1;
        } else if (midValue > value) {
          if (tempIndex === null || tempIndex > midIndex) {
            tempIndex = midIndex;
          }
          end2 = end2 - 1;
        }
      }
      return tempIndex;
    };
    const updateHeight = () => {
      vue.nextTick(() => {
        if (positions[positions.length - 1]) {
          const height = positions[positions.length - 1].bottom;
          listParentRef.value.style.height = height + "px";
        } else {
          listParentRef.value.style.height = "0px";
        }
      });
    };
    const setStartOffset = () => {
      vue.nextTick(() => {
        let startOffset = 0;
        if (start.value >= 1) {
          if (positions[start.value]) {
            const size = positions[start.value].top - (positions[start.value - aboveCount.value] ? positions[start.value - aboveCount.value].top : 0);
            startOffset = positions[start.value - 1].bottom - size;
          }
        }
        offset.value = startOffset;
      });
    };
    const handleScroll = () => {
      const { showLoading, scrollBottomLoading } = props;
      const container = containerRef.value;
      const scrollTop = container && container.scrollTop;
      start.value = getStartIndex(scrollTop);
      end.value = start.value + visibleCount.value;
      emits("scroll", scrollTop);
      if (showLoading) {
        if (scrollTop === 0) {
          isLoading.value = true;
          isTopOrBottom.value = "top";
          overflow.value = "hidden";
        } else if (scrollBottomLoading && scrollTop + container.clientHeight + 4 >= container.scrollHeight) {
          isLoading.value = true;
          isTopOrBottom.value = "bottom";
        } else {
          isLoading.value = false;
          isTopOrBottom.value = "";
        }
      }
      if (scrollTop + container.clientHeight >= container.scrollHeight - 1) {
        isScrollBottom = false;
      }
    };
    vue.watch(visibleData, () => {
      if (!listRef.value || listRef.value.length === 0) {
        return;
      }
      updatePositions();
      updateHeight();
      setStartOffset();
    });
    vue.watch(() => props.arrayList.length, (newVal, oldVal) => {
      isLoading.value = false;
      count = newVal - oldVal;
      if (count > 0) {
        if (containerRef.value.scrollTop === 0) {
          addTopItem(count);
          vue.nextTick(() => {
            const doms = document.querySelectorAll(".me-virtual-scroll-list-item");
            if (doms.length) {
              for (let i = 0; i < count; i++) {
                if (doms[i]) {
                  observer.observe(doms[i]);
                  setTimeout(() => {
                    observer.unobserve(doms[i]);
                    overflow.value = "auto";
                  }, 500);
                }
              }
            }
          });
        }
      } else {
        if (Math.abs(count) > 1) {
          vue.nextTick(() => {
            deletePatchItem();
          });
        } else {
          vue.nextTick(() => {
            deleteItem();
          });
        }
      }
    });
    const deletePatchItem = async () => {
      const { arrayList } = props;
      const arrayListIds = arrayList.reduce((list2, item) => {
        list2.push(item.id);
        return list2;
      }, []);
      const clonePositions = positions.slice(0);
      let deletePos = [];
      positions = clonePositions.filter((item) => {
        if (arrayListIds.includes(item.id)) {
          return true;
        } else {
          deletePos.push(item);
          return false;
        }
      });
      for (let i = 0; i < positions.length; i++) {
        positions[i].index = i;
      }
      ;
      if (deletePos.length > 0) {
        for (let count2 = 0; count2 < deletePos.length; count2++) {
          for (let i = deletePos[count2].index; i < positions.length; i++) {
            positions[i].top = positions[i].top - deletePos[count2].height;
            positions[i].bottom = positions[i].top - positions[i].height;
          }
          ;
          start.value = getStartIndex(containerRef.value.scrollTop);
          end.value = start.value + visibleCount.value;
          updateHeight();
          setStartOffset();
        }
      }
    };
    const addTopItem = (count2) => {
      if (listRef.value) {
        vue.nextTick(() => {
          const listDoms = document.querySelectorAll(".me-virtual-scroll-list-item");
          let arr = [];
          let scrollHeight = 0;
          let doms = [];
          for (let i = 0; i < count2; i++) {
            doms.push(listDoms[i]);
          }
          try {
            for (let i = 0; i < doms.length; i++) {
              const index2 = Number(doms[i].getAttribute("data-index"));
              const height = doms[i].getBoundingClientRect().height;
              const id = doms[i].id;
              scrollHeight += height;
              let top = 0, bottom = 0;
              if (i === 0) {
                top = 0;
                bottom = top + height;
              } else {
                top = arr[i - 1].bottom;
                bottom = top + height;
              }
              arr.push({
                index: index2,
                id,
                height,
                top,
                bottom
              });
            }
            positions = arr.concat(positions);
            for (let i = count2; i < positions.length; i++) {
              positions[i].index = i;
              positions[i].top = positions[i - 1].bottom;
              positions[i].bottom = positions[i].top + positions[i].height;
            }
            containerRef.value.scrollTop = scrollHeight;
            start.value = getStartIndex(containerRef.value.scrollTop);
            end.value = start.value + visibleCount.value;
            updateHeight();
            setStartOffset();
          } catch (err) {
          }
        });
      }
    };
    const deleteItem = () => {
      const { arrayList } = props;
      const arrayListIds = arrayList.reduce((list2, item) => {
        list2.push(item.id);
        return list2;
      }, []);
      let deletePos = null;
      positions.map((item, index2) => {
        if (!arrayListIds.includes(item.id)) {
          deletePos = item;
          positions.splice(index2, 1);
        }
      });
      if (deletePos) {
        for (let i = deletePos.index; i < positions.length; i++) {
          positions[i].top = positions[i].top - deletePos.height;
          positions[i].bottom = positions[i].bottom - deletePos.height;
        }
        ;
        start.value = getStartIndex(containerRef.value.scrollTop);
        end.value = start.value + visibleCount.value;
        updateHeight();
        setStartOffset();
      }
    };
    const updatePositions = () => {
      if (positions.length > 0) {
        startUpdatePositions();
      } else {
        startInitPositions();
      }
    };
    const startUpdatePositions = () => {
      listRef.value && listRef.value.forEach((node) => {
        const rect = node.getBoundingClientRect();
        const height = rect.height;
        const index2 = Number(node.getAttribute("data-index"));
        const oldHeight = positions[index2] ? positions[index2].height : 0;
        const value = oldHeight - height;
        if (value) {
          if (oldHeight === 0) {
            positions.push({
              index: index2,
              id: node.getAttribute("id"),
              height,
              top: positions[positions.length - 1].bottom,
              bottom: positions[positions.length - 1].bottom + height
            });
          } else {
            positions[index2].bottom = positions[index2].bottom - value;
            positions[index2].height = height;
          }
          for (let i = index2 + 1; i < positions.length; i++) {
            positions[i].top = positions[i - 1].bottom;
            positions[i].bottom = positions[i].bottom - value;
          }
        }
      });
      for (let i = 0; i < positions.length; i++) {
        positions[i].index = i;
      }
      ;
    };
    const startInitPositions = () => {
      let arr = [];
      let allHeight = 0;
      listRef.value && listRef.value.forEach((node, i) => {
        const index2 = Number(node.getAttribute("data-index"));
        const height = node.getBoundingClientRect().height;
        const id = node.id;
        allHeight += height;
        let top = 0, bottom = 0;
        if (i === 0) {
          top = 0;
          bottom = height;
        } else {
          top = arr[i - 1].bottom;
          bottom = top + height;
        }
        arr.push({
          index: index2,
          id,
          height,
          top,
          bottom
        });
      });
      positions = arr;
    };
    vue.onMounted(() => {
      screenHeight.value = containerRef.value.clientHeight;
      start.value = 0;
      end.value = start.value + visibleCount.value;
      vue.nextTick(() => {
        initPosition();
      });
    });
    const initPosition = () => {
      for (let i = 0; i < list.value.length; i++) {
        positions.push({
          index: list.value[i].index,
          id: props.arrayList[i].id,
          height: props.itemHeight,
          top: i * props.itemHeight,
          bottom: (i + 1) * props.itemHeight
        });
      }
    };
    const onImageLoad = (evt) => {
      const index2 = evt.target.getAttribute("data-index");
      if (isScrollBottom) {
        if (positions[index2] && !positions[index2].imageLoaded) {
          positions[index2].imageLoaded = true;
          scrollTo(containerRef.value.scrollHeight - containerRef.value.clientHeight);
        }
      }
    };
    const scrollTo = (offset2) => {
      if (offset2 >= 0) {
        containerRef.value.scrollTo({
          behavior: "smooth",
          top: offset2
        });
      }
    };
    const scrollToBottom = () => {
      let requestId;
      const go = () => {
        requestId = requestAnimationFrame(go);
        if (containerRef.value.scrollTop + containerRef.value.clientHeight >= containerRef.value.scrollHeight - 1) {
          cancelAnimationFrame(requestId);
          return;
        }
        scrollTo(containerRef.value.scrollHeight - containerRef.value.clientHeight);
      };
      go();
    };
    const getContainer = () => {
      return containerRef.value;
    };
    const scrollToIndex = (index2) => {
      if (positions[index2]) {
        let timerId;
        timerId = setInterval(() => {
          scrollTo(positions[index2].top);
          if (containerRef.value.scrollTop >= Math.floor(positions[index2].top)) {
            clearInterval(timerId);
            timerId = null;
          }
        }, 16);
      }
    };
    __expose({
      /** 滚动到底部 */
      scrollToBottom,
      /** 获取虚拟滚动容器DOM */
      getContainer,
      /** 滚动到指定的位置 */
      scrollTo,
      /** 滚动到指定的列表索引位置 */
      scrollToIndex
    });
    return (_ctx, _cache) => {
      return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
        isLoading.value ? (vue.openBlock(), vue.createElementBlock("div", {
          key: 0,
          class: vue.normalizeClass(["me-virtual-scroll-loading", [isTopOrBottom.value === "bottom" ? "bottom" : "top"]])
        }, [
          vue.createVNode(vue.unref(index.Loading), { tip: "加载中..." })
        ], 2)) : vue.createCommentVNode("", true),
        vue.createElementVNode("div", {
          ref_key: "containerRef",
          ref: containerRef,
          style: vue.normalizeStyle({ height: _ctx.height, overflow: overflow.value }),
          class: "me-virtual-scroll",
          onScroll: handleScroll
        }, [
          vue.createElementVNode("div", {
            class: "me-virtual-scroll-container",
            ref_key: "listParentRef",
            ref: listParentRef
          }, null, 512),
          vue.createElementVNode("div", {
            class: "me-virtual-scroll-list-box",
            style: vue.normalizeStyle({ transform: `translateY(${offset.value}px)` })
          }, [
            (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(visibleData.value, (item) => {
              return vue.openBlock(), vue.createElementBlock("div", {
                class: "me-virtual-scroll-list-item",
                ref_for: true,
                ref_key: "listRef",
                ref: listRef,
                key: item.index,
                id: item.item.id,
                "data-index": item.index
              }, [
                vue.renderSlot(_ctx.$slots, "default", {
                  item: item.item,
                  onLoad: onImageLoad,
                  index: item.index
                })
              ], 8, _hoisted_2);
            }), 128))
          ], 4)
        ], 36)
      ]);
    };
  }
});
exports.default = _sfc_main;
//# sourceMappingURL=virtual-scroll.vue2.cjs.map
